// aes-cbc.js  
const crypto = require('crypto');
const uuidv4 = require('uuid').v4;

// ============================
// 1. 参数准备 
// ============================
const algorithm = 'aes-256-cbc';    // aes-128-cbc / aes-192-cbc 也可 
// const key = crypto.randomBytes(32);    // 32B = 256bit；16B=128bit 
const key = Buffer.from("zj2F9#aU3$vE5nX3@qR7tY4wF2*hA1cR", 'utf8');
const iv = crypto.randomBytes(16);    // CBC IV 固定 16B 

const hmacAlgorithm = 'sha256';    // HMAC算法
const hmacKey = 'Kj8#mN5$pL9&vX3@qR4tY7wZ2*hC6bM1';    // HMAC密钥应独立于AES密钥 

function signature(signInput) {
  return crypto.createHmac(hmacAlgorithm, hmacKey).update(signInput).digest('hex');
}

// ============================
// 2. 加密函数 
// ============================
function encrypt(plainText) {
  console.log('IV for this encryption:', iv.toString('base64'));
  const cipher = crypto.createCipheriv(algorithm, key, iv);
  let encrypted = cipher.update(plainText, 'utf8', 'base64');
  encrypted += cipher.final('base64');
  return {
    encrypted,
    iv: iv.toString('base64')    // IV 也要发给对方 
  };
}

function encryptWithMetadata(plainText) {
  const nonce = uuidv4().replace(/-/g, '').slice(-32).toLowerCase(); // 随机数
  const timestamp = Math.floor(Date.now());
  const { encrypted, iv } = encrypt(plainText);

  // const signInput = `encrypted=${encrypted}&iv=${encodeURIComponent(iv)}&nonce=${nonce}&timestamp=${timestamp}`;
  const signInput = new URLSearchParams({
    encrypted,
    iv,
    nonce,
    timestamp
  }).toString();
  const sign = crypto.createHmac(hmacAlgorithm, hmacKey).update(signInput).digest('hex');

  return {
    encrypted,
    iv,
    nonce,
    timestamp,
    sign
  };
}

// ============================
// 3. 解密函数 
// ============================
function decrypt(packet) {
  const { encrypted, iv } = packet;
  console.log('IV for this decryption:', iv);
  const decipher = crypto.createDecipheriv(
    algorithm,
    key,
    Buffer.from(iv, 'base64')  // 注意这里要从Base64还原回Buffer
  );
  let decrypted = decipher.update(encrypted, 'base64', 'utf8');
  decrypted += decipher.final('utf8');
  return decrypted;
}

function decryptWithMetadata(packet) {
  const { encrypted, iv, nonce, timestamp, sign } = packet;

  // ----------1.防重放----------
  const now = Math.floor(Date.now());
  if (Math.abs(now - timestamp) > (60 * 2)) {   // ±2分钟容错可配置 
    throw new Error('timestamp too old');
  }

  // ----------2.验签----------
  // const signInput = `encrypted=${encrypted}&iv=${encodeURIComponent(iv)}&nonce=${nonce}&timestamp=${timestamp}`;
  const signInput = new URLSearchParams({
    encrypted,
    iv,
    nonce,
    timestamp
  }).toString();
  const expectSign = crypto.createHmac(hmacAlgorithm, hmacKey).update(signInput).digest('hex');

  if (expectSign !== sign) {
    throw new Error('invalid signature');
  }

  // ----------3.AES-CBC解密----------
  let decrypted = decrypt({ encrypted, iv });
  return decrypted;
}

// ============================
// 4. Demo 
// ============================
// const msg = 'Hello AES-CBC in Node.js';
// const enc = encrypt(msg);
// console.log('Encrypted:', JSON.stringify(enc, null, 2));

// const dec = decrypt(enc);
// console.log('Decrypted:', dec);

const test = decrypt({ iv: "xa3cw0A7xR8207G9z6QhNg==", encrypted: "zIM1ldsJQqZmxit6fuQtMaYNZ2lGJcCScULdVd7KwJYKf5LfVu7ToHOxFDogaOpOIWVvANpKqmGp/5T6xGaBpMuPXsg2hYPn7uT9lvSWPoVfcLWPnwtrAevVse/K+3jKVmpeq8I/0LJhBhAbd8DfKm+PPzo7I4wucK45ls2EzUyUKZoibtJ+chmfDkShgLJpcFE4/75MSElheW9FSPN99Tgs09ud5Dp0F85eBQrfzwBRV6OWcuH7CjOg87dxLqzilojRt0aYoVGo9dN5WtEJCftbFvjrdtKriSZRqKkzbKANHC36dVyRxGKMmnskxHIuh/vaaYlVSw5vjfdy062PHCAvO5vumWSQeoRMjfK3H+jLec0m8xbbeBptrKlNf3d4zU8Pwqe7sJaWYbq2r/BVT27SHsz1FMZy5soBq3B6t+R79tum+fEfVm9aF8qiWNQrEvI31LXRucMzyS2nMN2aEOoNCtE9wVQsAV0fRLgx9U49dRRWs5RThvM6gstsu/t2aNMpRul+Sbq2mnRwI5HsL74Bw1FfhkqqA0EQKCclm4/gCLdKtQFUmUO8WDijyNKxC+0y82AAhIKoVN5S1bPPYVEZtFH8LIA9TkosjwOn9xf750zTQYYkwUgrOudrQ2PJXws3q8fm68CvQ8cTOlLEELl9Y9TzhFJXR2FNgSF+MpR9racDNWEuXzQL3HTskTm8bBjbLcx0DARTJWzEsR0/YUMkiiG9Uih1a5q3s5/dVQZk6v4SqjcXs4tc5BMeBwvI1fu3gyesCUl74udgaQhekDHZHNgwe4Lim4du5SkuwDfDpCsnjTIBcfQJRPGOpxzLGkIbEz0DA8hpqNdfjPZ5/oH4Aqc1x+7REpxHI4mPiTaLaouHEXwPnkKFGlTmsOvlsVnaoSCIgOifjkJnJEeNQQzrxTivX2+nxBrfxeEa5RO6oxkV3R2liFTBjCsmygQv+WCWQhjUINawCgdg9JLp+yy76u/NG8Zyy8xxxWjdO2RdTKvUdVx7AYBPotRHDjHhWj5uxUFHFENACcTUX88DYNc7HJQKSAvedQidWyXjBPwvKua9I7Q9H3l106x8L+N3M1NDntnH2xa3bBuhoJQ3aABWXvCEuenXc7R7mehxSdSoSY+wz9yjl5/26Kpr3jLoCB4W0I9a/ogBTWQR8c4T4CPfhI0Y445FWpW2LPslAWNLAPVYtPk6j64KJHXH0ORLH3yB30+msLcwWjTuR3kM4T0pcQbmG5JtilsZCg98cuhicgEPS3Edm1l0tidLjhJoqO09TSjGsqO4x9v26953ALElJ0glR/M+qWcQTs+tDhD55Sod0QYzT8Zc0OzxFcDJ8Pbr2n+i3RGSZ7tIH8JdEsXNh6UceAUIuDbo5NycNyVJrNgqNOKjDDXzjjPNYtik+RwtJXgssot8sp0bucTkeXa9JM30kTdxP9URyxi58Cuuo+Tj12duwmj9ufMU2Ku9neMBiipFJ4qYxrvTffvlGocwIjUwa/y03BuWFVHvxsDuPLIl+zUfWAroB58tXWWV1OuoRR1NfvZgFiqrV822XvkJ3VX/9VTY+GcHP7VpwkNJH2xA/nR8gRW2K3jvHE2HxGLxT4HDoRti1f2vF+usB+OlijD7kVbESkvo+Spr+DxmKOQ/bOXSPCzX6pIqKRe5JcZNX7HLAQxutTgutyy4pcKq0WoHmLurIkkiUXCA6wBOAo0oV0UfH5PGl6MZJdKh8I7OpGUx700V137OOn/KilykYYCsGi/IPhFPitad719ZUS4fi85HLhHfuOegemM7I62VZSpNKNazsmTsYS1ljUCICZwqo6b60RMzcNgcYnk4aSAk4JY9n56dW9lCYKflC0YsYwwFSNWcxpjknk58hH6fPAIWKxcLhQCpTvKprd4yXXHJ95eetSMGM+mDDm8VA8jAAscrfBzWnC752rQ8r4wIirxY6ZNXiSZw54R234FRCMZ3gMuiPRHKJQnhXUSFLbiG29aqjxebDbcl8UQoJkd+UNguc3lZAP7CW0LgEEu4eSwPfsq1mnj5ytAG+8sdLNGjXAGgIfn6seX68J/leSGlZOtv5551f+KZKOP1l5xOFzSQcW9X4DPG1kBcEqIsHnQfHVJVONgaMJQK0BYjB6usvLfqhL2DSg2vxD40Gbk0GzGzTtOU+XvzPDaX97JcijIJKof8G1HdGYXUicYjrwa1j+S/r7IwW6QLq90KYenthI+GtohGuGOUGYFjMmumA8jJXQzkjg138HHnWysvFGIlKJdkoNwa3W9kNCLkrkO//tb2rhSo/w76+diBEoM4xhvtnoQe7rIUuRbRYqU7HyG3DnEEVf8VPMSjiQ/oaYssidgeZyntA7stLN3B8KRIvwutJgmdnyMDhZLwXqqsLvWeOcYBMQz6QKncuwF4AGphyraf0oDnBefmOXsZamAUCA11UFzkUJrXxse6HWlZGnjzOgJcSAe/4u8mtWxvrgYzV5rEcGOjQrNMWU+Lp6NhhnipHy/O6B4RZ9PPPoEtjE0EboZS0fveyjzivH0x5IOA/i5wuZZ0PUWKJSZ7fUMCOTEmtzU1T6oKIa4VBqQvZ43kn16wRkEy0P0/i6f1bQaH0BTUSRDaqfT6Cn4xuXYn6quOk+p3zlgpsagCwCnNOREYATCi9GN4H5h2WKpfoGJ5KMBBaq9BwQDO52nHmbjqKa8mj8cigZlExeUB8XzS5FQoHtdQW/rw5s0dE5wGPfM8cgY/aaX36i3qx2qCZMCFmy4acJrQs0WL8opcQJ8USbvIN82U76UwK62em2bD4kTdJIL+6GPhX1YAtNt4O+9XqQCWLUqGApKHTWFYGxnS3x1nNKk47iykp7ZKL0hvq833eAwBHQbUn1DPsmt5STUqUEA8YrlywmA7sjpffnCVVYYdYe5Quq1c0ZrOkk7OPqfVrL3KvXPmXeg0jmzxkhLBv31f7eXILozcjqQkL1CAV0LLZwmgs3gCVn2um6B4ZvJ4c0bwgR/pIybjOSPo5sqAsnW8TIomg2Durzbr4QDTSw==" });
console.log('Tested:', test);

// const msg2 = 'Hello AES-CBC with meta!';
// const enc2 = encryptWithMetadata(msg2);
// console.log('Encrypted With Metadata:', JSON.stringify(enc2, null, 2));

// const dec2 = decryptWithMetadata(enc2);
// console.log('Decrypted With Metadata:', dec2);

// const sign = signature('encrypted=NDF2taQENymCeom67H%2FADvB968ovr0omOdexrohXIiv7rZy%2BA2M2nZhzvGXjtXUS&iv=qB8IKU4Mb86EluRyqYB9Iw%3D%3D&nonce=c3ff8c865b5217f222721cd39932a224&timestamp=1760246898203');
// console.log('Signature:', sign);